package corewarsxxi.logic;

import corewarsxxi.logic.throwables.warriorHasNoProcesses;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.awt.Color;

/**
 * Warrior is the object that contains the program to be loaded and executed by
 * the Core.
 * This class handles the Memory, Processes, Loading and Storing a Warrior in to a file.
 *
 * @version 1.0
 * @see     corewarsxxi.logic.Warrior
 * @author  Luís Fonseca, ei10139 @ FEUP
 */
public class Warrior implements Serializable
{
    /** The name of the Warrior. */
    private String name;

    /** The Warrior Color */
    private Color c;

    /** The MemoryArray of the Warrior. */
    public MemoryArray program;

    /** The current processes of the Warrior. */
    public ProcessQueue processes;

    /** The extention used to store Warriors. */
    public final static String savExtention = ".redw";

    /**
     * Constructs a new Warrior with the predetermined name.
     * @param name  the name of the warrior
     */
    public Warrior(String name)
    {
        this.name = name;
        this.program = new MemoryArray(0);
        this.processes = new ProcessQueue();
    }

    /**
     * Sets the Warriors name
     * @param name  the name of the warrior
     */
    public void setName(String name)
    {
        this.name = name;
    }

    /**
     * Gets the Warriors name
     * @return the name
     */
    public String getName()
    {
        return this.name;
    }

    /**
     * Performs a cycle on the next process.
     * @throws warriorHasNoProcesses
     * @see corewarsxxi.logic.ProcessQueue
     */
    public void cycle() throws warriorHasNoProcesses
    {
        processes.cycle();
    }
    
    /**
     * Writes the Warrior properties - name and the program code - to a
     * virtual memory stream.
     * @return ByteArrayOutputStream containing the data of the Warrior.
     * @throws IOException  throws if there is insufficient memory RAM
     * @see corewarsxxi.logic.Warrior#save()
     */
    protected ByteArrayOutputStream psave() throws IOException
    {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);
        out.writeObject(this.name);
        out.writeObject(this.program);
        out.close();
        return bos;
    }

    /**
     * Stores the Warrior in a file with the predetermined extension.
     *
     * @param fname     The name of the file to be saved as. Extension will be
     *                  added automatically if not included.
     * @throws IOException  generic Input\Output error.
     * @see corewarsxxi.logic.Warrior#savExtention
     */
    public void save(String fname) throws IOException
    {
        if(!fname.endsWith(savExtention))
            fname += savExtention;
        
        ByteArrayOutputStream bos = psave();
        FileOutputStream fis = new FileOutputStream(fname);
        fis.write(bos.toByteArray());
        fis.close();
        bos.close();
    }

    /**
     * Loads the Warriors name and program from a virtual memory stream.
     * @param bis   the virtual memory stream.
     * @throws IOException              generic Input\Output error.
     * @throws ClassNotFoundException   throws if the warrior was saved using a previous version.
     * @see corewarsxxi.logic.Warrior#load()
     */
    protected void pload(ByteArrayInputStream bis) throws IOException, ClassNotFoundException
    {
        ObjectInputStream in = new ObjectInputStream(bis);
        this.name = (String)in.readObject();
        this.program = (MemoryArray)in.readObject();
        
        for(MemoryCell mf : this.program)
        {
            mf.opCode = mf.opCode.getSelf();
            mf.aField.aMode = mf.aField.aMode.getSelf();
            mf.bField.aMode = mf.bField.aMode.getSelf();
        }
        bis.close();
    }

    /**
     * Loads a Warrior from a file.
     * @param fname                     the file path
     * @throws FileNotFoundException    throws if file wasn't found.
     * @throws IOException              generic Input\Output error.
     * @throws ClassNotFoundException   throws if the warrior was saved using a previous version.
     */
    public void load(String fname) throws FileNotFoundException, IOException, ClassNotFoundException
    {
        File f = new File(fname);
        byte[] buf = new byte[(int)f.length()];
        ByteArrayInputStream bis = new ByteArrayInputStream(buf);
        FileInputStream fis = new FileInputStream(fname);

        fis.read(buf);
        bis.read(buf);
        fis.close();
        bis.reset();
        pload(bis);
    }

    /**
     * Gets the color of the warrior on the Core
     * @return the c
     */
    public Color getColor() {
        return this.c;
    }

    /**
     * Sets the color of this warrior on the Core
     * @param c the Color to set
     */
    protected void setColor(Color c) {
        this.c = c;
    }
    
    /**
     * Overrides the default Object.toString() function so it displays the warriors name.
     * @return a String with the warriors name.
     */
    @Override
    public String toString()
    {
        return "Warrior [" + this.getName() + "]";
    }
}
